/*
 * parameters.cpp
 *
 *  Created on: May 26, 2011
 *      Author: nzhao
 */

#include "core/globalvars.h"
#include "share/parameters.h"

char	CONFIG_FILE_NAME[50];
char	INPUT_PATH[200];
char	INPUT_FILE_NAME[100];
char	PULSE_PATH[200];
char	OUTPUT_PATH[200];
char	GROUP_ID[20];

double	EXT_MAG_FIELD;
double   THETA;
double   PHI;
int      STATE_1;
int		STATE_2;
int      CALCULATED_ORDER;

double   MIN_TIME;
double   MAX_TIME;
int      NTIME;
char     STR_SEQ_NAME[10];
int      PULSE_NUM;
char	 	INPUT_PACK_NAME[100];
double	DrvDetuning;
double DrvRabi;

int		STATE_SEED;
double   THRESHOLDVALUE;
double	MEDIATED_INT_THRESHOLDVALUE;
double   AMPLITUDE_VALUE;
int		APPROXIMATION;
double	EXP_DECAY_RATE;
int		TASK_MODE;
int      CACHE_Q;
int		IS_BLK;
int		IsMediated;
int      IsCore;

char    	VERSION_INFO[100];
char    	EXEC[200];
char    	OUTPUT_FILE_NAME[500];

double  	TIME_STEP;
int			IsDriving = 0;


int			sample_seed_setQ = 0;
int			len_para = 0;
int			is_input_path = 0;
int			is_output_path = 0;
int			is_pulsepack_path = 0;
int			is_pulsepack_name = 0;
int 		is_set_u = 0;


void check_error();

void Load_Config()
{

    double threshold_val;
    char path[200], name[100], val[200];
    string line;
    char * cstr;
    int nline, i;

    const char delimiters[] = "= \t";
    char *token, *cp;

    strcpy(path, WORKSHOP);
    strcat(path, "config/");
    strcat(path, CONFIG_FILE_NAME);
    strcat(path, ".cfg");

    ifstream config(path);
    if (!config)
    {

    	cout << "***** ERROR ***** " << endl;
    	cout << "Cannot load config. file: " << path << endl;
    	assert(0);
    }

    config >> nline;
    if(nline < len_para)
    	cout << "WARNING: not all parameters are provided in the configuration file." << endl;

    for (i = 0; i < nline+1; i++)
    {
        getline(config, line);
        if (i > 0)
        {
            cstr = new char [line.size() + 1];
            strcpy(cstr, line.c_str());

            cp = strdupa(cstr);

            token = strtok(cp, delimiters);
            strcpy(name, token);

            token = strtok(NULL, delimiters);
            strcpy(val, token);

            delete [] cstr;


/////////////////////////////////////////////////////////////////
// I/O Path (3)
            if(strcmp("InputPath",name) == 0)
            {
                strcpy(INPUT_PATH, val);
                is_input_path = 1;
            }
            if(strcmp("OutputPath",name) == 0)
            {
                strcpy(OUTPUT_PATH, val);
                is_output_path = 1;
            }
            if(strcmp("PulsePath",name) == 0)
            {
                strcpy(PULSE_PATH, val);
                is_pulsepack_path = 1;
            }

/////////////////////////////////////////////////////////////////
// I/O Path (3)
            if(strcmp("Input",name) == 0)
            {
                strcpy(INPUT_FILE_NAME, val);
            }
            if(strcmp("Group",name) == 0)
            {
                strcpy(GROUP_ID, val);
            }
            if(strcmp("Package",name) == 0)
            {
            	strcpy(INPUT_PACK_NAME, val);

            	if(strcmp("None", INPUT_PACK_NAME) == 0)
            		is_pulsepack_name = 0;
            	else
            		is_pulsepack_name = 1;
            }

/////////////////////////////////////////////////////////////////
// Magnetic Field and Electronspin States (5)
            if(strcmp("MagB",name) == 0)
            {
                EXT_MAG_FIELD   = atof(val);
            }
            if(strcmp("Theta",name) == 0)
            {
                THETA = atof(val) / 180. * PI;
            }
            if(strcmp("Phi",name) == 0)
            {
                PHI = atof(val) / 180. * PI;
            }
            if(strcmp("State1",name) == 0)
            {
                STATE_1 = atoi(val);
            }
            if(strcmp("State2",name) == 0)
            {
                STATE_2 = atoi(val);
            }

/////////////////////////////////////////////////////////////////
//Time Sequence (3)
            if(strcmp("Start",name) == 0)
            {
                MIN_TIME = atof(val);
            }
            if(strcmp("End",name) == 0)
            {
                MAX_TIME = atof(val);
            }
            if(strcmp("Ntime",name) == 0)
            {
                NTIME = atoi(val);
            }

/////////////////////////////////////////////////////////////////
// Pulse Sequence (4)
            if(strcmp("Pulse",name) == 0)
            {
                strcpy(STR_SEQ_NAME, val);
            }
            if(strcmp("NumPulse",name) == 0)
            {
                PULSE_NUM = atoi(val);
            }
            if(strcmp("DrvDetuning",name) == 0)
            {
                DrvDetuning = atof(val);
            }
            if(strcmp("DrvRabi",name) == 0)
            {
            	DrvRabi = atof(val);
            }
/////////////////////////////////////////////////////////////////
// CCE Control (12)

            if(strcmp("CCE",name) == 0)
            {
                CALCULATED_ORDER = atoi(val);
            }
            if(strcmp("Seed",name) == 0 )
            {
                STATE_SEED = atoi(val);
                sample_seed_setQ = 1;
            }
            if(strcmp("Threshold",name) == 0)
            {
            	THRESHOLDVALUE = atof(val);
//                THRESHOLDVALUE	= threshold_val*threshold_val;
            }
            if(strcmp("MediatedThreshold",name) == 0)
            {
            	MEDIATED_INT_THRESHOLDVALUE = atof(val);
            }
            if(strcmp("Singular",name) == 0)
            {
                AMPLITUDE_VALUE = atof(val);
            }
            if(strcmp("Approximation",name) == 0)
            {
            	APPROXIMATION = atoi(val);
            }
            if(strcmp("DecayRate",name) == 0)
            {
            	EXP_DECAY_RATE = atoi(val);
            }
            if(strcmp("Task",name) == 0)
            {
            	TASK_MODE = atoi(val);
            }
            if(strcmp("Cache",name) == 0)
            {
                CACHE_Q = atoi(val);
            }
            if(strcmp("BLOCK_Q",name) == 0)
            {
                IS_BLK = atoi(val);
            }
            if(strcmp("Mediated",name) == 0)
            {
                IsMediated = atoi(val);
            }
            if(strcmp("FPdata",name) == 0)
            {
                IsCore = atoi(val);
            }
        }
    }
    config.close();
}

void ParameterResolve(int argc, char ** argv)
{
    const char options[] = "r:B:T:P:p:l:n:c:S:E:k:D:I:f:g:x:y:u:s:a:R:m:W:G:qCLM";
    len_para  = 28+3-1;//the parameter numbers can be recognized in the Configuration file: changeable + unchangeable(paths) - 1(-r).

	//////////////////////////////////////////////////////
	//load config
    int paraFlag, is_default_config;

    is_default_config = 1;
    strcpy(CONFIG_FILE_NAME, "Config");//default config file name

    optind = 1;
    while ((paraFlag = getopt (argc, argv, options)) != -1)
    {
        if(paraFlag == 'r')
        {
            strcpy(CONFIG_FILE_NAME, optarg);
            is_default_config = 0;
        }
    }
    Load_Config();

    //////////////////////////////////////////////////////
	//source code version info
    char ver[100], ver_str1[50], ver_str2[50], ver_str3[50];

    strcpy(ver, WORKSHOP);
    strcat(ver, "bin/Version.log");
    ifstream version(ver);
    if (!version) assert(0);

    version >> ver_str1 >> ver_str2 >> ver_str3;
    cout << "Src version:\t"	<< ver_str1 << " "<<  ver_str2 << " " << ver_str3 << endl;
    strcpy(VERSION_INFO, ver_str1);
    strcat(VERSION_INFO, ver_str2);
    strcat(VERSION_INFO, ver_str3);

    version.close();

    //////////////////////////////////////////////////////
    //parse parameters
    double threshold_val;

    strcpy(EXEC, argv[0]);
    strcpy(OUTPUT_FILE_NAME,"Res");



    optind = 1;
    while ((paraFlag = getopt (argc, argv, options)) != -1)
    {
        switch (paraFlag)
        {
        	//data group name
			case 'g':
				strcpy(GROUP_ID, optarg);
				strcat(OUTPUT_FILE_NAME,"_g");
				strcat(OUTPUT_FILE_NAME, optarg);
				break;
				//configuration file
			case 'r':
				strcat(OUTPUT_FILE_NAME,"_r");
				strcat(OUTPUT_FILE_NAME, optarg);
				break;
			//input file name
			case 'f':
				strcpy(INPUT_FILE_NAME, optarg);
				strcat(OUTPUT_FILE_NAME,"_f");
				strcat(OUTPUT_FILE_NAME, optarg);
				break;
            // pulse package
            case 'l':
            	strcpy(INPUT_PACK_NAME, optarg);
            	strcat(OUTPUT_FILE_NAME,"_l");
            	strcat(OUTPUT_FILE_NAME, optarg);
            	is_pulsepack_name = 1;
            	break;

            case 'W'://detuning of RF (nuclear spin) driving field: omegaRF-omegaZeeman
                DrvDetuning = 2.0 * PI * atof(optarg);
                strcat(OUTPUT_FILE_NAME,"_W");
                strcat(OUTPUT_FILE_NAME, optarg);
                break;

            case 'G'://Rabi frequency of RF driving field
                DrvRabi = 2.0 * PI * atof(optarg);
                strcat(OUTPUT_FILE_NAME,"_G");
                strcat(OUTPUT_FILE_NAME, optarg);
                IsDriving = 1;
                break;

            //External magnetic field
            case 'B':
                EXT_MAG_FIELD   = atof(optarg);
                strcat(OUTPUT_FILE_NAME,"_B");
                strcat(OUTPUT_FILE_NAME, optarg);
                break;
            case 'T':
                THETA = atof(optarg) / 180. * PI;
                strcat(OUTPUT_FILE_NAME,"_T");
                strcat(OUTPUT_FILE_NAME, optarg);
                break;
            case 'P':
                PHI = atof(optarg) / 180. * PI;
                strcat(OUTPUT_FILE_NAME,"_P");
                strcat(OUTPUT_FILE_NAME, optarg);
                break;
            case 'x':
                STATE_1 = atoi(optarg);
                strcat(OUTPUT_FILE_NAME,"_x");
                strcat(OUTPUT_FILE_NAME, optarg);
                break;
            case 'y':
            	STATE_2 = atoi(optarg);
            	strcat(OUTPUT_FILE_NAME,"_y");
            	strcat(OUTPUT_FILE_NAME, optarg);
            	break;
            	//CCE order
            case 'c':
            	CALCULATED_ORDER = atoi(optarg);
            	assert(CALCULATED_ORDER >= 1);
            	strcat(OUTPUT_FILE_NAME,"_c");
            	strcat(OUTPUT_FILE_NAME, optarg);
            	break;


            //Time range and number
            case 'S':
                MIN_TIME = atof(optarg);
                assert(MIN_TIME >= 0.);
                strcat(OUTPUT_FILE_NAME,"_S");
                strcat(OUTPUT_FILE_NAME, optarg);
                break;
            case 'E':
                MAX_TIME = atof(optarg);
                strcat(OUTPUT_FILE_NAME,"_E");
                strcat(OUTPUT_FILE_NAME, optarg);
                break;
            case 'k':
            	NTIME = atoi(optarg);
            	strcat(OUTPUT_FILE_NAME,"_k");
            	strcat(OUTPUT_FILE_NAME, optarg);
            	break;
            	//pulse type and number
            case 'p':
            	strcpy(STR_SEQ_NAME, optarg);
            	strcat(OUTPUT_FILE_NAME,"_p");
            	strcat(OUTPUT_FILE_NAME, optarg);
            	break;
            case 'n':
            	PULSE_NUM = atoi(optarg);
            	strcat(OUTPUT_FILE_NAME,"_n");
            	strcat(OUTPUT_FILE_NAME, optarg);
            	break;
            case 's':
                STATE_SEED = atoi(optarg);
                strcat(OUTPUT_FILE_NAME,"_s");
                strcat(OUTPUT_FILE_NAME, optarg);
                sample_seed_setQ = 1;
                break;

            //Bath cut-off distance
            case 'D':
            	THRESHOLDVALUE = atof(optarg);
                strcat(OUTPUT_FILE_NAME,"_D");
                strcat(OUTPUT_FILE_NAME, optarg);
                break;
            case 'I':
                MEDIATED_INT_THRESHOLDVALUE = atof(optarg);
                strcat(OUTPUT_FILE_NAME,"_I");
                strcat(OUTPUT_FILE_NAME, optarg);
                break;
            case 'u':
                AMPLITUDE_VALUE = atof(optarg);
                is_set_u = 1;
                strcat(OUTPUT_FILE_NAME,"_u");
                strcat(OUTPUT_FILE_NAME, optarg);
                break;
            case 'q':
                CACHE_Q = 1;
                strcat(OUTPUT_FILE_NAME,"_q");
                break;
        	case 'L':
        		IS_BLK = 1;
        		strcat(OUTPUT_FILE_NAME,"_L");
        		break;
            case 'M':
                IsMediated = 1;
                strcat(OUTPUT_FILE_NAME,"_M");
                break;
            case 'C':
                IsCore = 1;
                strcat(OUTPUT_FILE_NAME,"_C");
                break;
            case 'a':
            	APPROXIMATION = atoi(optarg);
                strcat(OUTPUT_FILE_NAME,"_a");
                strcat(OUTPUT_FILE_NAME, optarg);
                break;
            case 'R':
            	EXP_DECAY_RATE = atof(optarg);
                strcat(OUTPUT_FILE_NAME,"_R");
                strcat(OUTPUT_FILE_NAME, optarg);
                break;
            case 'm':
            	TASK_MODE = atoi(optarg);
                strcat(OUTPUT_FILE_NAME,"_m");
                strcat(OUTPUT_FILE_NAME, optarg);
                break;

            case '?':
                if (isprint (optopt))
                    fprintf (stderr, "Unknown option `-%c'.\n", optopt);
                else
                    fprintf (stderr, "Unknown option character `\\x%x'.\n",optopt);
                abort ();
            default:
                abort ();
        }
    }

    check_error();

    if(TASK_MODE != 4)
    {
        // input number in the unit of mu_sec, convert to ms, so that consist with energy unit, (energy unit: k rad / sec)
    	MAX_TIME /= 1000.0;
    	MIN_TIME /= 1000.0;
    }

    TIME_STEP = ( MAX_TIME - MIN_TIME ) / NTIME;

    cout << "Version info:\t"	<< VERSION_INFO << endl;
    cout << "The job task:\t"	<< argv[0] << endl;

    cout << "Config. file:\t"   << CONFIG_FILE_NAME << endl;
    cout << "Input file:\t"		<< INPUT_PATH << INPUT_FILE_NAME << ".xyz" << endl;
    if( is_pulsepack_name == 1 && is_pulsepack_path == 1)
    	cout << "Pack file name:\t" << PULSE_PATH << INPUT_PACK_NAME << ".ppk" << endl;
    cout << "Output file:\t"	<< OUTPUT_PATH << OUTPUT_FILE_NAME << ".dat" << endl;
    cout << "Group Id:\t"       << GROUP_ID << endl;

    cout << "Extern magB (Tesla):\t"	<< EXT_MAG_FIELD << endl;
    cout << "Theta angle (Degree):\t"	<< THETA << endl;
    cout << "Phi angle (Degree):\t"     << PHI << endl;
    cout << "State 1:\t"				<< STATE_1 << endl;
    cout << "State 2:\t"				<< STATE_2 << endl;

    cout << "Time range:\t"				<< MIN_TIME << "~" << MAX_TIME<< endl;
    cout << "Time point:\t"				<< NTIME << endl;

    cout << "Pulse type:\t"				<< STR_SEQ_NAME << endl;
    cout << "Pulse number:\t"			<< PULSE_NUM << endl;
    cout << "IsDriving:\t"				<< IsDriving << endl;

    cout << "CCE order:\t"				<< CALCULATED_ORDER << endl;
    if(TASK_MODE == 1)
    	cout << "Initial state seed:\t" << STATE_SEED << endl;
    cout << "Threshold:\t"	<< THRESHOLDVALUE << endl;
    cout << "Mediated Threshold:\t"	<< MEDIATED_INT_THRESHOLDVALUE << endl;
    cout << "Amplitude:\t"      << AMPLITUDE_VALUE << endl;
    cout << "Approximation:\t"	<< APPROXIMATION << endl;
    cout << "Decay rate:\t"		<< EXP_DECAY_RATE<< endl;
    cout << "Task Mode:\t"		<< TASK_MODE << endl;
    cout << "Cache_Q:\t"        << CACHE_Q << endl;
    cout << "Block_Q:\t"        << IS_BLK << endl;
    cout << "Mediated_Q:\t"		<< IsMediated << endl;
    cout << "FirstPrinciple Data:\t" << IsCore << endl;
}

void check_error()
{
    if( is_input_path == 0)
    {
    	cout << "ERROR: input path is not provided in the configuration file." << endl;
    	assert(0);
    }
    if( is_output_path== 0)
    {
    	cout << "ERROR: output path is not provided in the configuration file." << endl;
    	assert(0);
    }
    if( is_pulsepack_name == 1 && is_pulsepack_path == 0)
    {
    	cout << "ERROR: Pulse package file name assigned without path." << endl;
    	assert(0);
    }

    if(TASK_MODE == 1 && sample_seed_setQ == 0)
    {
    	cout << "WARNING: single sample seed is not set. default seed 1 is used." << endl;
    	STATE_SEED = 1;
    }

    if(is_set_u && !CACHE_Q)
    {
        cout << "Programe does not cache cce data. '-u' does not apply, since CACHE_Q == 0." << endl;
    }

    if(IsDriving && DrvDetuning >= 2.0 * PI * 100.)
    {
    	cout << "Too large detuning" << endl;
    	assert(0);
    }
}

